const STATUS = Symbol('status')
const VALUE = Symbol('value')
const PRIVATE_VALUE = Symbol('_value')
const PRIVATE_PROMISE = Symbol('_promise')
const THEN_FN = Symbol('thenFn')
const CATCH_FN = Symbol('errFn')
const FINIALLY_FN = Symbol('finially')
const QUEUE = Symbol('queue')

const noop = () => {}

const STATUS_MAP = Object.create({
  Pending: 'pending',
  Fulfilled: "fulfilled",
  Rejected: "rejected"
})

class PromiseA {
  static all(iterable) {
    return Reflect.construct(this, [(resolve, reject) => {
      const transArr = Array.from(iterable);
      let resolvedCount = 0;
      const res = new Proxy([], {
        set: function (...rest) {
          Reflect.set(...rest)
          if (Object.is(transArr.length, resolvedCount)) {
            resolve(res);
          }
          return true
        }
      });
      transArr.forEach((item, index) => {
        if (item instanceof this) {
          const setValue = function (value) {
            resolvedCount++;
            res[index] = value
          }
          item.then(setValue).catch(reject)
        } else {
          resolvedCount++;
          res[index] = item;
        }
      })
    }])
  }

  static allSettled(iterable) {
    return Reflect.construct(this, [(resolve, reject) => {
      const transArr = Array.from(iterable);
      let resolvedCount = 0;
      const res = new Proxy([], {
        set: function (...rest) {
          Reflect.set(...rest)
          if (Object.is(transArr.length, resolvedCount)) {
            resolve(res);
          }
          return true
        }
      });
      transArr.forEach((item, index) => {
        if (item instanceof this) {
          const setValue = function (value) {
            resolvedCount++;
            res[index] = {
              status: STATUS_MAP.Fulfilled,
              value: value
            };
          }

          const setError = function (error) {
            resolvedCount++;
            res[index] = {
              status: STATUS_MAP.Rejected,
              reason: error
            }
          }
          item.then(setValue).catch(setError)
        } else {
          resolvedCount++;
          res[index] = {
            status: STATUS_MAP.Fulfilled,
            value: item
          };
        }
      })
    }])
  }

  static race(iterable) {
    return Reflect.construct(this, [(resolve, reject) => {
      const transArr = Array.from(iterable);
      transArr.forEach((item) => {
        if (item instanceof this) {
          item = item.then(value => {
            resolve(value);
            // 将 resolve 失效
            resolve = noop;
            reject = noop;
          }).catch(reject)
        } else {
          resolve(item);
          // 将 resolve 失效
          resolve = noop;
          reject = noop;
        }
      })
    }])
  }

  static reject(errValue) {
    const promsie = Reflect.construct(this, [noop])
    const promiseProxy = new Proxy(promsie, {
      set: function (obj, prop, v) {
        switch (prop) {
          case THEN_FN:
          case CATCH_FN:
          case FINIALLY_FN:
            setTimeout(() => {
              Reflect.set(obj, STATUS, STATUS_MAP.Rejected)
              Reflect.set(obj, VALUE, errValue)
            }, 0);
            break
        }
        return Reflect.set(obj, prop, v)
      }
    })
    return promiseProxy
  }

  static resolve(value) {
    const promsie = Reflect.construct(this, [noop])
    const promiseProxy = new Proxy(promsie, {
      set: function (obj, prop, v) {
        switch (prop) {
          case THEN_FN:
          case CATCH_FN:
          case FINIALLY_FN:
            setTimeout(() => {
              Reflect.set(obj, STATUS, STATUS_MAP.Fulfilled)
              Reflect.set(obj, VALUE, value)
            }, 0);
            break
        }
        return Reflect.set(obj, prop, v)
      }
    })
    return promiseProxy
  }

  constructor(fn) {
    // 初始化状态值
    this[STATUS] = STATUS_MAP.Pending

    const resolve = (value) => {
      setTimeout(() => {
        Reflect.set(this, STATUS, STATUS_MAP.Fulfilled)
        Reflect.set(this, VALUE, value)
      }, 0);
    }

    const reject = (errorValue) => {
      setTimeout(() => {
        Reflect.set(this, STATUS, STATUS_MAP.Rejected)
        Reflect.set(this, VALUE, errorValue)
      }, 0);
    }

    fn(resolve, reject)
  }

  // 私有值
  [PRIVATE_VALUE]
  // 读取私有值
  get[VALUE]() {
    return this[PRIVATE_VALUE]
  }

  set[VALUE](value) {
    // 1、判断状态 2、调用 then 方法里的 fn or catch 里的 errfn
    this[PRIVATE_VALUE] = value
    switch (this[STATUS]) {
      case STATUS_MAP.Fulfilled:
        if (this[THEN_FN]) {
          try {
            const thenValue = this[THEN_FN](value)
            if (this[PRIVATE_PROMISE]) {
              Reflect.set(this[PRIVATE_PROMISE], STATUS, STATUS_MAP.Fulfilled)
              Reflect.set(this[PRIVATE_PROMISE], VALUE, thenValue)
            }
          } catch (error) {
            if (this[PRIVATE_PROMISE]) {
              Reflect.set(this[PRIVATE_PROMISE], STATUS, STATUS_MAP.Rejected)
              Reflect.set(this[PRIVATE_PROMISE], VALUE, error)
            }
          }
        }
        break;
      case STATUS_MAP.Rejected:
        if (this[CATCH_FN]) {
          const catchValue = this[CATCH_FN](value)
          this[PRIVATE_PROMISE] && Reflect.set(this[PRIVATE_PROMISE], VALUE, catchValue)
        }
        // 根据链追踪 catch 方法
        for (let target = this; true;) {
          if (target[PRIVATE_PROMISE]) {
            if (target[PRIVATE_PROMISE][CATCH_FN]) {
              const catchValue = target[PRIVATE_PROMISE][CATCH_FN](value)
              target = target[PRIVATE_PROMISE]
              Reflect.set(target[PRIVATE_PROMISE], STATUS, STATUS_MAP.Fulfilled)
              Reflect.set(target[PRIVATE_PROMISE], VALUE, catchValue)
              break
            } else {
              target = target[PRIVATE_PROMISE]
            }
          } else {
            break
          }
        }
        break;
      default:
        break
    }

    this[FINIALLY_FN] && this[FINIALLY_FN]()
  }

  then(onFulfilled, onRejected) {
    const privatePromise = Reflect.construct(this.constructor, [noop])

    // 保存 Promise 的引用
    this[PRIVATE_PROMISE] = privatePromise

    // 保存 then Function
    onFulfilled && (this[THEN_FN] = onFulfilled)

    // 保存 catch Function 
    onRejected && (this[CATCH_FN] = onRejected)

    return privatePromise
  }

  catch (onRejected) {
    return this.then(noop, onRejected)
  }

  // 
  finally(onFinally) {
    this[FINIALLY_FN] = onFinally
  }
}

// export default PromiseA

const promise1 = PromiseA.resolve(3);
const promise2 = new PromiseA((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

PromiseA.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));